/*
 * @Author: xiaosihan
 * @Date: 2021-04-10 02:44:01
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2021-10-22 23:55:10
 */

import _ from "lodash";

function bezier(arr: [number, number, number, number], t: number) {
    var ut = 1 - t;
    var a1 = arr[1] * ut + arr[2] * t;
    var result = ((arr[0] * ut + arr[1] * t) * ut + a1 * t) * ut + (a1 * ut + (arr[2] * ut + arr[3] * t) * t) * t;
    return result;
}

interface TParam { [key: string]: number | number[] | any }

// interface Transition extends TParam { }

// 过渡动画插件
export default class Transition {

    /**
     *Creates an instance of transition.
     * @param {{ [key: string]: number }} // 初始值
     * @param {[0.8, 0, 0.2, 1]} // 贝塞尔曲线
     * @param {number} [duration=1000]  // 过渡时间
     * @memberof transition
     */
    constructor(
        param: TParam = {},
        bezier: [number, number, number, number] = [0.8, 0, 0.2, 1],
        duration: number = 500
    ) {

        this.setDuration(duration);

        this.setBezier(bezier);

        this.set(param);

        // 初始化定时更新时间戳
        if (Transition.isInit) {
            Transition.isInit = false;
            setInterval(() => {
                Transition.TimeTemp = performance.now();
            }, 40);
        }
    }

    // 所有的 key 的枚举
    _keys: Array<string | number> = [];

    // 贝塞尔曲线
    _bezier: [number, number, number, number] = [0.8, 0, 0.2, 1];

    // x轴贝塞尔
    _Xbezier: [number, number, number, number] = [0, 0.8, 0.2, 1];

    // y轴贝塞尔
    _Ybezier: [number, number, number, number] = [0, 0, 1, 1];

    //TODO 贝塞尔采样计算
    setBezier(bezier: [number, number, number, number] = [0.8, 0, 0.2, 1]) {
        this._bezier = _.clone(bezier);
        this._Xbezier = [0, bezier[0], bezier[2], 1];
        this._Ybezier = [0, bezier[1], bezier[3], 1];
        return this;
    }

    // 过渡时间
    _duration = 0;

    // 设置过渡时间
    setDuration(duration = 500) {
        this._duration = duration;
        return this;
    }

    // 私有变量 x_start x_now x_end x_startTime;
    _: { [key: string]: any } = {};

    static isInit = true;

    // 获取时间戳
    static TimeTemp = performance.now();

    // 计算当前 key 的值
    get(key: string | number): number {

        key = String(key);

        if (this._keys.includes(key) && Transition.TimeTemp < (this._[key + "_startTime"] + this._duration)) {

            let start = this._[key + "_start"];

            let end = this._[key + "_end"];

            let time_progress = _.clamp(
                (Transition.TimeTemp - this._[key + "_startTime"]) / this._duration,
                0,
                1
            );

            // 斜率
            let t = 0;

            // 无限接近法 求斜率t
            while (bezier(this._Xbezier, t + 0.1) < time_progress) {
                t += 0.1;
            }
            while (bezier(this._Xbezier, t + 0.01) < time_progress) {
                t += 0.01;
            }

            t = _.clamp(t, 0, 1); // 斜率最大为1

            // 通过斜率求y值
            return (bezier(this._Ybezier, t) * (end - start)) + start;

        } else if (this._keys.includes(key) && Transition.TimeTemp >= (this._[key + "_startTime"] + this._duration)) {

            return this._[key + "_end"];
        }

        // `没有 ${key} 的值`;
        return 0;
    }

    set(param: TParam) {

        let timetemp = Transition.TimeTemp;

        for (let key in param) {

            // 有就赋值
            if (this._keys.includes(key) && param[key] !== this._[key + "_end"]) {

                this._[key + "_start"] = this.get(key);

                this._[key + "_end"] = param[key];
                // 起始时间
                this._[key + "_startTime"] = timetemp;

                // 执行状态未完成
                this._isDone = false;

                // 没有就添加一个
            } else if (!this._keys.includes(key)) {

                this._[key + "_start"] = param[key];

                this._[key + "_end"] = param[key];
                // 起始时间
                this._[key + "_startTime"] = timetemp - this._duration;

                // 执行状态未完成
                this._isDone = false;

                // 添加新的访问器
                // Object.defineProperties(this, {
                //     [key]: {
                //         set(v) {
                //             this.set({ [key]: v });
                //         },
                //         get() {
                //             return this.get(key);
                //         }
                //     }
                // });

            }

            if (!this._keys.includes(key)) {
                this._keys.push(key);
            }

            // 执行状态未完成
            this._isDone = false;

        }
        return this;
    }

    // 设置起始位置
    setStart(param: TParam) {
        for (let key in param) {
            // 有才赋值
            if (this._keys.includes(key)) {
                this._[key + "_start"] = param[key];
            }
        }
        return this;
    }

    // 设置结束位置
    setEnd(param: TParam) {
        for (let key in param) {
            // 有才赋值
            if (this._keys.includes(key)) {
                this._[key + "_end"] = param[key];
            }
        }
        return this;
    }

    reset(param: TParam) {

        this.setStart(param);
        this.setEnd(param);
        for (let i in param) {
            this._[i + "_startTime"] = -this._duration;
        }
        this._isDone = true;
        return this;
    }

    // 获取过渡对象上面的key
    getKeys() {
        return _.clone(this._keys);
    }

    hasKey(key: string) {
        return this._keys.includes(key);
    }

    getJson() {
        let keys = this.getKeys();

        let json: any = {};

        keys.map(key => {
            json[key] = this.get(key);
        });
        return json;
    }

    _isDone: boolean = false;

    // 是否执行完成了
    isDone(): boolean {

        if (this._isDone) {
            return true;
        }

        let isisDone = false;

        for (let i in this._keys) {

            isisDone = this._[this._keys[i] + "_startTime"] + this._duration < Transition.TimeTemp;

            // 只要有一个没有完成 都算没有完成 过渡
            if (!isisDone) {
                return false;
            }
        }

        if (!this._isDone) {
            this._isDone = true;
            return false;
        }

        return true;
    }
}


